#include "WindowsWindow.h"
#include "Core/Log.h"
#include "Events/ApplicationEvent.h"
#include "Events/MouseEvent.h"
#include "Events/KeyEvent.h"
#include "Platform/OpenGL/OpenGLContext.h"
#include "Debug/Instrumentor.h"
#include "Core/Base.h"
#include "Renderer/Renderer.h"
#include "Core/KeyCodes.h"

namespace Hazel {
    static bool s_GLFWInitialized = false;
    static uint8_t s_GLFWWindowCount = 0;

    static void GLFWErrorCallBack(int error ,const char* description){
        HZ_CORE_ERROR("GLFW Error ({0}:{1})",error,description);
    };
//    Scope<Window> Window::Create(const WindowProps& props){
//        return CreateScope<WindowsWindow>(props);
//    }


    WindowsWindow::WindowsWindow(const WindowProps& props){
        HZ_PROFILE_FUNCTION();
        Init(props);
    }

    WindowsWindow::~WindowsWindow(){
        HZ_PROFILE_FUNCTION();
        Shutdown();
    }

    void WindowsWindow::Init(const WindowProps& props){
        HZ_PROFILE_FUNCTION();
        m_Data.Title = props.Title;
        m_Data.Width = props.Width;
        m_Data.Height = props.Height;

        HZ_CORE_INFO("create window {0} ({1} ,{2})",props.Title,props.Width,props.Height);

        if (s_GLFWWindowCount == 0){
            HZ_PROFILE_SCOPE("glfwInit");
            int success = glfwInit();
            HZ_CORE_ASSERT(success,"clould not init GLFW")
            glfwSetErrorCallback(GLFWErrorCallBack );
            s_GLFWInitialized = true;
        }
        {
            HZ_PROFILE_SCOPE("glfwCreateWindow");
#if defined(HZ_DEBUG)
            if (Renderer::GetAPI() == RendererAPI::API::OpenGL)
				glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE);
#endif
            m_Window = glfwCreateWindow((int)props.Width, (int)props.Height, m_Data.Title.c_str(), nullptr, nullptr);
            ++s_GLFWWindowCount;
        }

//        m_Window = glfwCreateWindow((int) props.Width,(int)props.Height,m_Data.Title.c_str(), nullptr, nullptr);
        m_Context = GraphicsContext::Create(m_Window);
        m_Context->Init();
        glfwMakeContextCurrent(m_Window);

        int status = gladLoadGLLoader((GLADloadproc) glfwGetProcAddress);
        HZ_CORE_ASSERT(status, "Failed to initialize Glad!");

        glfwSetWindowUserPointer(m_Window,&m_Data);
        SetVSync(true);

        glfwSetWindowSizeCallback(m_Window,[](GLFWwindow* window,int width,int height){
            WindowData& data  = *(WindowData*) glfwGetWindowUserPointer(window);
            data.Width = width;
            data.Height = height;
            WindowResizeEvent event(width,height);
            data.EventCallback(event);
        });

        glfwSetWindowCloseCallback(m_Window,[](GLFWwindow* window){
            WindowData& data  = *(WindowData*) glfwGetWindowUserPointer(window);
            WindowCloseEvent event;
            data.EventCallback(event);
        });

        glfwSetKeyCallback(m_Window,[](GLFWwindow* window,int key,int scancode,int action,int mods){
            WindowData& data = *(WindowData*) glfwGetWindowUserPointer(window);
            switch (action) {
                case GLFW_PRESS:
                {
                    KeyPressedEvent event(static_cast<KeyCode>(key),0);
                    data.EventCallback(event);
                    break;
                }
                case GLFW_RELEASE:
                {
                    KeyReleasedEvent event(static_cast<KeyCode>(key));
                    data.EventCallback(event);
                    break;
                }

                case GLFW_REPEAT:
                {
                    KeyPressedEvent event(static_cast<KeyCode>(key),1);
                    data.EventCallback(event);
                    break;
                }
            }
        });

        glfwSetCharCallback(m_Window, [](GLFWwindow* window, unsigned int keycode)
        {
            WindowData& data = *(WindowData*)glfwGetWindowUserPointer(window);

            KeyTypedEvent event(static_cast<KeyCode>(keycode));
            data.EventCallback(event);
        });

        glfwSetMouseButtonCallback(m_Window,[](GLFWwindow* window,int button,int action,int mods){
            WindowData& data = *(WindowData*) glfwGetWindowUserPointer(window);
            switch (action) {
                case GLFW_PRESS:
                {
                    MouseButtonPressedEvent event(static_cast<MouseCode>(button));
                    data.EventCallback(event);
                    break;
                }
                case GLFW_RELEASE:
                {
                    MouseButtonPressedEvent event(static_cast<MouseCode>(button));
                    data.EventCallback(event);
                    break;
                }
            }
        });

        glfwSetScrollCallback(m_Window,[](GLFWwindow* window,double xOffset,double yOffset){
            WindowData& data = *(WindowData*) glfwGetWindowUserPointer(window);
            MouseScrolledEvent event((float)xOffset,(float)yOffset);
            data.EventCallback(event);
        });

        glfwSetCursorPosCallback(m_Window,[](GLFWwindow* window,double xPos,double yPos){
            WindowData& data = *(WindowData*) glfwGetWindowUserPointer(window);
            MouseMovedEvent event((float)xPos,(float)yPos);
            data.EventCallback(event);
        });
    }

    void WindowsWindow::Shutdown() {
        HZ_PROFILE_FUNCTION();
        glfwDestroyWindow(m_Window);
        if (--s_GLFWWindowCount == 0)
        {
            HZ_CORE_INFO("Terminating GLFW");
            glfwTerminate();
        }
    }
    void WindowsWindow::OnUpdate() {
        HZ_PROFILE_FUNCTION();
        glfwPollEvents();
        m_Context->SwapBuffers();
//        glfwSwapBuffers(m_Window);
    }

    void WindowsWindow::SetVSync(bool enabled) {
        HZ_PROFILE_FUNCTION();
        if(enabled){
            glfwSwapInterval(1);
        }else{
            glfwSwapInterval(0);
        }

        m_Data.VSync = enabled;
    }

    bool WindowsWindow::IsVSync() const {
        return m_Data.VSync;
    }

    void WindowsWindow::GetPixelSize(uint32_t& width, uint32_t& height) const {
        glfwGetFramebufferSize(m_Window, reinterpret_cast<int *>(&width), reinterpret_cast<int *>(&height));
    }
}





















